home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / pine / ttyin.c < prev    next >
C/C++ Source or Header  |  1996-03-14  |  64KB  |  2,394 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: ttyin.c,v 4.121 1996/03/15 07:13:42 hubert Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. /*======================================================================
  43.        ttyin.c
  44.        Things having to do with reading from the tty driver and keyboard
  45.           - initialize tty driver and reset tty driver
  46.           - read a character from terminal with keyboard escape seqence mapping
  47.           - initialize keyboard (keypad or such) and reset keyboard
  48.           - prompt user for a line of input
  49.           - read a command from keyboard with timeouts.
  50.  
  51.  ====*/
  52.  
  53.  
  54. /*
  55.  * Helpful definitions
  56.  */
  57. #define    RETURN_CH(X)    return(key_recorder((X), 0))
  58.  
  59.  
  60. #if    !(defined(DOS) || defined(OS2))
  61. /* Beginning of giant switch between UNIX and DOS input driver */
  62.  
  63. #include "headers.h"
  64.  
  65. /*
  66.  * Internal prototypes
  67.  */
  68. void line_paint PROTO((int, int *));
  69. int  process_config_input PROTO((int *));
  70. int  getchar_for_kbseq PROTO ((void));
  71. int  check_for_timeout PROTO((int));
  72. int  read_a_char PROTO((void));
  73. void read_bail PROTO((void));
  74.  
  75. #ifdef USE_POLL
  76. #include <stropts.h>
  77. #include <poll.h>
  78. #endif
  79.  
  80. #ifdef HAVE_TERMIOS
  81. #include <termios.h>
  82.  
  83. struct termios _raw_tty, _original_tty;
  84.  
  85. #else
  86. #ifdef HAVE_TERMIO
  87. #include <termio.h>
  88.  
  89. static struct termio _raw_tty, _original_tty;
  90.  
  91.  
  92. #else /* HAVE_TERMIO */
  93.  
  94. #include <sgtty.h>
  95.  
  96. static struct sgttyb  _raw_tty,     _original_tty;
  97. static struct ltchars _raw_ltchars, _original_ltchars;
  98. static struct tchars  _raw_tchars,  _original_tchars;
  99. static int            _raw_lmode,   _original_lmode;
  100. #endif /* HAVE_TERMIO  */
  101. #endif /* HAVE_TERMIOS */
  102.  
  103. /*
  104.  * current raw state
  105.  */
  106. static short _inraw = 0;
  107.  
  108.  
  109. #define STDIN_FD    0
  110. #define STDOUT_FD    1
  111.  
  112. /*----------------------------------------------------------------------
  113.     Initialize the tty driver to do single char I/O and whatever else  (UNIX)
  114.  
  115.    Args:  struct pine
  116.  
  117.  Result: tty driver is put in raw mode so characters can be read one
  118.          at a time. Returns -1 if unsuccessful, 0 if successful.
  119.  
  120. Some file descriptor voodoo to allow for pipes across vforks. See 
  121. open_mailer for details.
  122.   ----------------------------------------------------------------------*/
  123. init_tty_driver(ps)
  124.      struct pine *ps;
  125. {
  126. #ifdef    MOUSE
  127.     if(F_ON(F_ENABLE_MOUSE, ps_global))
  128.       if(init_mouse())
  129.     kpinsert(&ps->kbesc, "\033[M", KEY_XTERM_MOUSE);
  130. #endif    /* MOUSE */
  131.  
  132.     /* turn off talk permission by default */
  133.     
  134.     if(F_ON(F_ALLOW_TALK, ps))
  135.       allow_talk(ps);
  136.     else
  137.       disallow_talk(ps);
  138.  
  139.     return(Raw(1));
  140. }
  141.  
  142.  
  143. /*----------------------------------------------------------------------
  144.     Add the hardwired escape sequences to the keyboard escape sequence    (UNIX)
  145.     tree.  This only needs to be here for people who don't have correct
  146.     termcap/info entries for these, which is almost everybody.  Since
  147.     these get added after the termcap/info versions of the same thing,
  148.     an escape sequence here that is the same as one there, or is a prefix
  149.     of one, or is a superset of one, will override the one from termcap.
  150.     So an incorrect termcap won't break things.  A correct termcap entry
  151.     that gets overwrote by an unfortunate coincidence will break things.
  152.     In order to reverse the priority and have termcap/info entries override
  153.     hardwired ones, define TERMCAP_WINS for os_unix.c in the
  154.     pico subdirectory.
  155.  
  156.    Args:  kbesc -- the trie used to store the escape sequences in
  157.  
  158.  Result: a fixed set of known escape sequences are added to the trie
  159.   ----------------------------------------------------------------------*/
  160. void
  161. setup_dflt_esc_seq(kbesc)
  162.     KBESC_T     **kbesc;
  163. {
  164.     /*
  165.      * this is sort of a hack [no kidding], but it allows us to use
  166.      * the function keys on pc's running telnet
  167.      *
  168.      * UW-NDC/UCS vt10[01] application mode.
  169.      */
  170.     kpinsert(kbesc, "\033OP", PF1);
  171.     kpinsert(kbesc, "\033OQ", PF2);
  172.     kpinsert(kbesc, "\033OR", PF3);
  173.     kpinsert(kbesc, "\033OS", PF4);
  174.     kpinsert(kbesc, "\033Op", PF5);
  175.     kpinsert(kbesc, "\033Oq", PF6);
  176.     kpinsert(kbesc, "\033Or", PF7);
  177.     kpinsert(kbesc, "\033Os", PF8);
  178.     kpinsert(kbesc, "\033Ot", PF9);
  179.     kpinsert(kbesc, "\033Ou", PF10);
  180.     kpinsert(kbesc, "\033Ov", PF11);
  181.     kpinsert(kbesc, "\033Ow", PF12);
  182.  
  183.     /*
  184.      * DEC vt100, ANSI and cursor key mode.
  185.      */
  186.     kpinsert(kbesc, "\033OA", KEY_UP);
  187.     kpinsert(kbesc, "\033OB", KEY_DOWN);
  188.     kpinsert(kbesc, "\033OC", KEY_RIGHT);
  189.     kpinsert(kbesc, "\033OD", KEY_LEFT);
  190.  
  191.     /*
  192.      * special keypad functions
  193.      */
  194.     kpinsert(kbesc, "\033[4J", KEY_PGUP);
  195.     kpinsert(kbesc, "\033[3J", KEY_PGDN);
  196.     kpinsert(kbesc, "\033[2J", KEY_HOME);
  197.     kpinsert(kbesc, "\033[N",  KEY_END);
  198.  
  199.     /*
  200.      * ANSI mode.
  201.      */
  202.     kpinsert(kbesc, "\033[=a", PF1);
  203.     kpinsert(kbesc, "\033[=b", PF2);
  204.     kpinsert(kbesc, "\033[=c", PF3);
  205.     kpinsert(kbesc, "\033[=d", PF4);
  206.     kpinsert(kbesc, "\033[=e", PF5);
  207.     kpinsert(kbesc, "\033[=f", PF6);
  208.     kpinsert(kbesc, "\033[=g", PF7);
  209.     kpinsert(kbesc, "\033[=h", PF8);
  210.     kpinsert(kbesc, "\033[=i", PF9);
  211.     kpinsert(kbesc, "\033[=j", PF10);
  212.     kpinsert(kbesc, "\033[=k", PF11);
  213.     kpinsert(kbesc, "\033[=l", PF12);
  214.  
  215.     /*
  216.      * DEC vt100, ANSI and cursor key mode reset
  217.      */
  218.     kpinsert(kbesc, "\033[A", KEY_UP);
  219.     kpinsert(kbesc, "\033[B", KEY_DOWN);
  220.     kpinsert(kbesc, "\033[C", KEY_RIGHT);
  221.     kpinsert(kbesc, "\033[D", KEY_LEFT);
  222.  
  223.     /*
  224.      * DEC vt52 mode.
  225.      */
  226.     kpinsert(kbesc, "\033A", KEY_UP);
  227.     kpinsert(kbesc, "\033B", KEY_DOWN);
  228.     kpinsert(kbesc, "\033C", KEY_RIGHT);
  229.     kpinsert(kbesc, "\033D", KEY_LEFT);
  230.  
  231.     /*
  232.      * DEC vt52 application keys, and some Zenith 19.
  233.      */
  234.     kpinsert(kbesc, "\033?r", KEY_DOWN);
  235.     kpinsert(kbesc, "\033?t", KEY_LEFT);
  236.     kpinsert(kbesc, "\033?v", KEY_RIGHT);
  237.     kpinsert(kbesc, "\033?x", KEY_UP);
  238.  
  239.     /*
  240.      * Sun Console sequences.
  241.      */
  242.     kpinsert(kbesc, "\033[1",   KEY_SWALLOW_Z);
  243.     kpinsert(kbesc, "\033[215", KEY_SWAL_UP);
  244.     kpinsert(kbesc, "\033[217", KEY_SWAL_LEFT);
  245.     kpinsert(kbesc, "\033[219", KEY_SWAL_RIGHT);
  246.     kpinsert(kbesc, "\033[221", KEY_SWAL_DOWN);
  247.  
  248.     /*
  249.      * Kermit App Prog Cmd, gobble until ESC \ (kermit should intercept this)
  250.      */
  251.     kpinsert(kbesc, "\033_", KEY_KERMIT);
  252.  
  253.     /*
  254.      * Fake a control character.
  255.      */
  256.     kpinsert(kbesc, "\033\033", KEY_DOUBLE_ESC);
  257. }
  258.  
  259.  
  260.  
  261. /*----------------------------------------------------------------------
  262.    Set or clear the specified tty mode
  263.  
  264.    Args: ps --  struct pine
  265.      mode -- mode bits to modify
  266.      clear -- whether or not to clear or set
  267.  
  268.  Result: tty driver mode change. 
  269.   ----------------------------------------------------------------------*/
  270. void
  271. tty_chmod(ps, mode, func)
  272.     struct pine *ps;
  273.     int         mode;
  274.     int         func;
  275. {
  276.     char    *tty_name;
  277.     int         new_mode;
  278.     struct stat  sbuf;
  279.     static int   saved_mode = -1;
  280.  
  281.     /* if no problem figuring out tty's name & mode? */
  282.     if((((tty_name = (char *) ttyname(STDIN_FD)) != NULL
  283.      && fstat(STDIN_FD, &sbuf) == 0)
  284.     || ((tty_name = (char *) ttyname(STDOUT_FD)) != NULL
  285.         && fstat(STDOUT_FD, &sbuf) == 0))
  286.        && !(func == TMD_RESET && saved_mode < 0)){
  287.     new_mode = (func == TMD_RESET)
  288.              ? saved_mode
  289.              : (func == TMD_CLEAR)
  290.             ? (sbuf.st_mode & ~mode)
  291.             : (sbuf.st_mode | mode);
  292.     /* assign tty new mode */
  293.     if(chmod(tty_name, new_mode) == 0){
  294.         if(func == TMD_RESET)        /* forget we knew */
  295.           saved_mode = -1;
  296.         else if(saved_mode < 0)
  297.           saved_mode = sbuf.st_mode;    /* remember original */
  298.     }
  299.     }
  300. }
  301.  
  302.  
  303.  
  304. /*----------------------------------------------------------------------
  305.        End use of the tty, put it back into it's normal mode     (UNIX)
  306.  
  307.    Args: ps --  struct pine
  308.  
  309.  Result: tty driver mode change. 
  310.   ----------------------------------------------------------------------*/
  311. void
  312. end_tty_driver(ps)
  313.      struct pine *ps;
  314. {
  315.     ps = ps; /* get rid of unused parameter warning */
  316.  
  317. #ifdef    MOUSE
  318.     end_mouse();
  319. #endif    /* MOUSE */
  320.     fflush(stdout);
  321.     dprint(2, (debugfile, "about to end_tty_driver\n"));
  322.  
  323.     tty_chmod(ps, 0, TMD_RESET);
  324.     Raw(0);
  325. }
  326.  
  327.  
  328.  
  329. /*----------------------------------------------------------------------
  330.     Actually set up the tty driver                             (UNIX)
  331.  
  332.    Args: state -- which state to put it in. 1 means go into raw, 0 out of
  333.  
  334.   Result: returns 0 if successful and -1 if not.
  335.   ----*/
  336.  
  337. Raw(state)
  338. int state;
  339. {
  340.     /** state is either ON or OFF, as indicated by call **/
  341.     /* Check return code only on first call. If it fails we're done for and
  342.        if it goes OK the other will probably go OK too. */
  343.  
  344.     if (state == 0 && _inraw) {
  345.         /*----- restore state to original -----*/
  346. #ifdef    HAVE_TERMIOS
  347.     if (tcsetattr (STDIN_FD, TCSADRAIN, &_original_tty) < 0)
  348.         return -1;
  349. #else    /* HAVE_TERMIOS */
  350. #ifdef    HAVE_TERMIO
  351.         if(ioctl(STDIN_FD, TCSETAW, &_original_tty) < 0)
  352.           return(-1);
  353. #else
  354.     if(ioctl(STDIN_FD, TIOCSETP, &_original_tty) < 0)
  355.           return(-1);
  356.     (void) ioctl(STDIN_FD, TIOCSLTC, &_original_ltchars);
  357.     (void) ioctl(STDIN_FD, TIOCSETC, &_original_tchars);
  358.         (void) ioctl(STDIN_FD, TIOCLSET, &_original_lmode);
  359. #endif    /* HAVE_TERMIO */
  360. #endif    /* HAVE_TERMIOS */
  361.         _inraw = 0;
  362.     } else if (state == 1 && ! _inraw) {
  363.         /*----- Go into raw mode (cbreak actually) ----*/
  364.  
  365. #ifdef    HAVE_TERMIOS
  366.     if (tcgetattr (STDIN_FD, &_original_tty) < 0)
  367.         return -1;
  368.     tcgetattr (STDIN_FD, &_raw_tty);
  369.     _raw_tty.c_lflag &= ~(ICANON | ECHO | IEXTEN); /* noecho raw mode    */
  370.  
  371.      _raw_tty.c_lflag &= ~ISIG;        /* disable signals           */
  372.      _raw_tty.c_iflag &= ~ICRNL;        /* turn off CR->NL on input  */
  373.      _raw_tty.c_oflag &= ~ONLCR;        /* turn off NL->CR on output */
  374.  
  375.     /* Only go into 8 bit mode if we are doing something other
  376.      * than plain ASCII. This will save the folks that have
  377.      * their parity on their serial lines wrong thr trouble of
  378.      * getting it right
  379.      */
  380.         if(ps_global->VAR_CHAR_SET && ps_global->VAR_CHAR_SET[0] &&
  381.        strucmp(ps_global->VAR_CHAR_SET, "us-ascii"))
  382.       _raw_tty.c_iflag &= ~ISTRIP;        /* turn off hi bit stripping */
  383.  
  384.     _raw_tty.c_cc[VMIN]  = '\01';        /* min # of chars to queue  */
  385.     _raw_tty.c_cc[VTIME] = '\0';        /* min time to wait for input*/
  386.     _raw_tty.c_cc[VINTR] = 0;
  387.     _raw_tty.c_cc[VQUIT] = 0;
  388.     _raw_tty.c_cc[VSUSP] = 0;
  389.     ps_global->low_speed = cfgetospeed(&_raw_tty) < B4800;
  390.     tcsetattr (STDIN_FD, TCSADRAIN, &_raw_tty);
  391. #else
  392. #ifdef    HAVE_TERMIO
  393.         if(ioctl(STDIN_FD, TCGETA, &_original_tty) < 0)
  394.           return(-1);
  395.     (void) ioctl(STDIN_FD, TCGETA, &_raw_tty);    /** again! **/
  396.  
  397.     _raw_tty.c_lflag &= ~(ICANON | ECHO);    /* noecho raw mode  */
  398.  
  399.      _raw_tty.c_lflag &= ~ISIG;        /* disable signals */
  400.  
  401.      _raw_tty.c_iflag &= ~ICRNL;        /* turn off CR->NL on input  */
  402.      _raw_tty.c_oflag &= ~ONLCR;        /* turn off NL->CR on output */
  403.     _raw_tty.c_cc[VMIN]  = 1;        /* min # of chars to queue   */
  404.     _raw_tty.c_cc[VTIME] = 0;        /* min time to wait for input*/
  405.     _raw_tty.c_cc[VINTR] = 0;
  406.     _raw_tty.c_cc[VQUIT] = 0;
  407.         ps_global->low_speed = (_raw_tty.c_cflag&CBAUD) < (unsigned int)B4800;
  408.     (void) ioctl(STDIN_FD, TCSETAW, &_raw_tty);
  409.  
  410. #else    /* HAVE_TERMIO */
  411.         if(ioctl(STDIN_FD, TIOCGETP, &_original_tty) < 0)
  412.           return(-1);
  413.     (void) ioctl(STDIN_FD, TIOCGETP, &_raw_tty);   
  414.         (void) ioctl(STDIN_FD, TIOCGETC, &_original_tchars);
  415.     (void) ioctl(STDIN_FD, TIOCGETC, &_raw_tchars);
  416.     (void) ioctl(STDIN_FD, TIOCGLTC, &_original_ltchars);
  417.     (void) ioctl(STDIN_FD, TIOCGLTC, &_raw_ltchars);
  418.         (void) ioctl(STDIN_FD, TIOCLGET, &_original_lmode);
  419.         (void) ioctl(STDIN_FD, TIOCLGET, &_raw_lmode);
  420.  
  421.     _raw_tty.sg_flags &= ~(ECHO);    /* echo off */
  422.     _raw_tty.sg_flags |= CBREAK;    /* raw on    */
  423.         _raw_tty.sg_flags &= ~CRMOD;    /* Turn off CR -> LF mapping */
  424.  
  425.     _raw_tchars.t_intrc = -1;    /* Turn off ^C and ^D */
  426.     _raw_tchars.t_eofc  = -1;
  427.  
  428. #ifdef    DEBUG
  429.     if(debug < 9)            /* only on if full debugging set */
  430. #endif
  431.     _raw_tchars.t_quitc = -1;    /* Turn off ^\ */
  432.  
  433.     _raw_ltchars.t_lnextc = -1;    /* Turn off ^V so we can use it */
  434.     _raw_ltchars.t_dsuspc = -1;    /* Turn off ^Y so we can use it */
  435.     _raw_ltchars.t_suspc  = -1;    /* Turn off ^Z; we just read 'em */
  436.     _raw_ltchars.t_werasc = -1;    /* Turn off ^w word erase */
  437.     _raw_ltchars.t_rprntc = -1;    /* Turn off ^R reprint line */
  438.         _raw_ltchars.t_flushc = -1;    /* Turn off ^O output flush */
  439.  
  440.     /* Only go into 8 bit mode if we are doing something other
  441.      * than plain ASCII. This will save the folks that have
  442.      * their parity on their serial lines wrong thr trouble of
  443.      * getting it right
  444.      */
  445.         if(ps_global->VAR_CHAR_SET && ps_global->VAR_CHAR_SET[0] &&
  446.        strucmp(ps_global->VAR_CHAR_SET, "us-ascii")) {
  447.         _raw_lmode |= LPASS8;
  448. #ifdef    NXT                /* Hope there aren't many like this! */
  449.             _raw_lmode |= LPASS8OUT;
  450. #endif
  451.     }
  452.             
  453.     (void) ioctl(STDIN_FD, TIOCSETP, &_raw_tty);
  454.     (void) ioctl(STDIN_FD, TIOCSLTC, &_raw_ltchars);
  455.         (void) ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
  456.         (void) ioctl(STDIN_FD, TIOCLSET, &_raw_lmode);
  457.         ps_global->low_speed =  (int)_raw_tty.sg_ispeed < B4800;
  458.  
  459. #endif
  460. #endif
  461.     _inraw = 1;
  462.     crlf_proc(0);
  463.     xonxoff_proc(F_ON(F_PRESERVE_START_STOP, ps_global));
  464.     }
  465.     return(0);
  466. }
  467.  
  468.  
  469.  
  470. /*----------------------------------------------------------------------
  471.     Set up the tty driver to use XON/XOFF flow control         (UNIX)
  472.  
  473.    Args: state -- True to make sure XON/XOFF turned on, FALSE off.
  474.  
  475.   Result: none.
  476.  
  477.   ----*/
  478. void
  479. xonxoff_proc(state)
  480.     int state;
  481. {
  482.     if(_inraw){
  483.     if(state){
  484. #ifdef    HAVE_TERMIOS
  485.         if(!(_raw_tty.c_iflag & IXON)){
  486.         _raw_tty.c_iflag |= IXON;
  487.         tcsetattr (STDIN_FD, TCSADRAIN, &_raw_tty);
  488.         }
  489. #else
  490. #ifdef    HAVE_TERMIO
  491.         if(!(_raw_tty.c_iflag & IXON)){
  492.         _raw_tty.c_iflag |= IXON;    /* turn ON ^S/^Q on input   */
  493.         (void) ioctl(STDIN_FD, TCSETAW, &_raw_tty);
  494.         }
  495. #else    /* HAVE_TERMIO */
  496.         if(_raw_tchars.t_startc == -1 || _raw_tchars.t_stopc == -1){
  497.         _raw_tchars.t_startc = 'Q' - '@'; /* Turn ON ^S/^Q */
  498.         _raw_tchars.t_stopc  = 'S' - '@';
  499.         (void) ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
  500.         }
  501. #endif
  502. #endif
  503.     }
  504.     else if(F_OFF(F_PRESERVE_START_STOP, ps_global)){
  505. #ifdef    HAVE_TERMIOS
  506.         if(_raw_tty.c_iflag & IXON){
  507.         _raw_tty.c_iflag &= ~IXON;    /* turn off ^S/^Q on input   */
  508.         tcsetattr (STDIN_FD, TCSADRAIN, &_raw_tty);
  509.         }
  510. #else
  511. #ifdef    HAVE_TERMIO
  512.         if(_raw_tty.c_iflag & IXON){
  513.         _raw_tty.c_iflag &= ~IXON;    /* turn off ^S/^Q on input   */
  514.         (void) ioctl(STDIN_FD, TCSETAW, &_raw_tty);
  515.         }
  516. #else    /* HAVE_TERMIO */
  517.         if(!(_raw_tchars.t_startc == -1 && _raw_tchars.t_stopc == -1)){
  518.         _raw_tchars.t_startc = -1;    /* Turn off ^S/^Q */
  519.         _raw_tchars.t_stopc  = -1;
  520.         (void) ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
  521.         }
  522. #endif
  523. #endif
  524.     }
  525.     }
  526. }
  527.  
  528.  
  529.  
  530. /*----------------------------------------------------------------------
  531.     Set up the tty driver to do LF->CR translation        (UNIX)
  532.  
  533.    Args: state -- True to turn on translation, false to write raw LF's
  534.  
  535.   Result: none.
  536.  
  537.   ----*/
  538. void
  539. crlf_proc(state)
  540.     int state;
  541. {
  542.     if(_inraw){
  543.     if(state){                /* turn ON NL->CR on output */
  544. #ifdef    HAVE_TERMIOS
  545.         if(!(_raw_tty.c_oflag & ONLCR)){
  546.         _raw_tty.c_oflag |= ONLCR;
  547.         tcsetattr (STDIN_FD, TCSADRAIN, &_raw_tty);
  548.         }
  549. #else
  550. #ifdef    HAVE_TERMIO
  551.         if(!(_raw_tty.c_oflag & ONLCR)){
  552.         _raw_tty.c_oflag |= ONLCR;
  553.         (void) ioctl(STDIN_FD, TCSETAW, &_raw_tty);
  554.         }
  555. #else    /* HAVE_TERMIO */
  556.         if(!(_raw_tty.sg_flags & CRMOD)){
  557.         _raw_tty.sg_flags |= CRMOD;
  558.         (void) ioctl(STDIN_FD, TIOCSETP, &_raw_tty);
  559.         }
  560. #endif
  561. #endif
  562.     }
  563.     else{                    /* turn OFF NL-CR on output */
  564. #ifdef    HAVE_TERMIOS
  565.         if(_raw_tty.c_oflag & ONLCR){
  566.         _raw_tty.c_oflag &= ~ONLCR;
  567.         tcsetattr (STDIN_FD, TCSADRAIN, &_raw_tty);
  568.         }
  569. #else
  570. #ifdef    HAVE_TERMIO
  571.         if(_raw_tty.c_oflag & ONLCR){
  572.         _raw_tty.c_oflag &= ~ONLCR;
  573.         (void) ioctl(STDIN_FD, TCSETAW, &_raw_tty);
  574.         }
  575. #else    /* HAVE_TERMIO */
  576.         if(_raw_tty.sg_flags & CRMOD){
  577.         _raw_tty.sg_flags &= ~CRMOD;
  578.         (void) ioctl(STDIN_FD, TIOCSETP, &_raw_tty);
  579.         }
  580. #endif
  581. #endif
  582.     }
  583.     }
  584. }
  585.  
  586.  
  587. /*----------------------------------------------------------------------
  588.     Set up the tty driver to hanle interrupt char        (UNIX)
  589.  
  590.    Args: state -- True to turn on interrupt char, false to not
  591.  
  592.   Result: tty driver that'll send us SIGINT or not
  593.  
  594.   ----*/
  595. void
  596. intr_proc(state)
  597.     int state;
  598. {
  599.     if(_inraw){
  600.     if(state){
  601. #ifdef HAVE_TERMIOS
  602.         _raw_tty.c_lflag |= ISIG;        /* enable signals */
  603.         _raw_tty.c_cc[VINTR] = ctrl('C');
  604.         tcsetattr(STDIN_FD, TCSADRAIN, &_raw_tty);
  605. #else
  606. #ifdef HAVE_TERMIO
  607.         _raw_tty.c_lflag |= ISIG;        /* enable signals */
  608.         _raw_tty.c_cc[VINTR] = ctrl('C');
  609.         (void)ioctl(STDIN_FD, TCSETAW, &_raw_tty);
  610. #else
  611.         _raw_tchars.t_intrc = ctrl('C');
  612.         (void)ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
  613. #endif /* HAVE_TERMIO */
  614. #endif /* HAVE_TERMIOS */
  615.     }
  616.     else{
  617. #ifdef    HAVE_TERMIOS
  618.         _raw_tty.c_lflag &= ~ISIG;        /* disable signals */
  619.         _raw_tty.c_cc[VINTR] = 0;
  620.         tcsetattr(STDIN_FD, TCSADRAIN, &_raw_tty);
  621. #else
  622. #ifdef    HAVE_TERMIO
  623.         _raw_tty.c_lflag &= ~ISIG;        /* disable signals */
  624.         _raw_tty.c_cc[VINTR] = 0;
  625.         (void)ioctl(STDIN_FD, TCSETAW, &_raw_tty);
  626. #else    /* HAVE_TERMIO */
  627.         _raw_tchars.t_intrc = -1;
  628.         (void)ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
  629. #endif /* HAVE_TERMIO */
  630. #endif /* HAVE_TERMIOS */
  631.     }
  632.     }
  633. }
  634.  
  635.  
  636. #ifdef RESIZING
  637. jmp_buf winch_state;
  638. int     winch_occured = 0;
  639. int     ready_for_winch = 0;
  640. #endif
  641.  
  642. /*----------------------------------------------------------------------
  643.      This checks whether or not a character            (UNIX)
  644.      is ready to be read, or it times out.
  645.  
  646.     Args:  time_out --  number of seconds before it will timeout
  647.  
  648.   Result: Returns a NO_OP_IDLE or a NO_OP_COMMAND if the timeout expires
  649.       before input is available, or a KEY_RESIZE if a resize event
  650.       occurs, or READY_TO_READ if input is available before the timeout.
  651.   ----*/
  652. int
  653. check_for_timeout(time_out)
  654.      int time_out;
  655. {
  656. #ifdef USE_POLL
  657.      struct pollfd pollfd;
  658. #else
  659.      struct timeval tmo;
  660.      fd_set         readfds, errfds;
  661. #endif
  662.      int        res;
  663.      unsigned char  c;
  664.  
  665.      fflush(stdout);
  666.  
  667. #ifdef RESIZING
  668.      if(winch_occured || setjmp(winch_state) != 0) {
  669.          ready_for_winch = 0;
  670.      fix_windsize(ps_global);
  671. #ifdef POSIX_SIGNALS
  672.      /*
  673.       * Need to unblock signal after longjmp from handler, because
  674.       * signal is normally unblocked upon routine exit from the handler.
  675.       */
  676.      if(!winch_occured)
  677.        sigrelse(SIGWINCH);
  678. #endif
  679.          winch_occured   = 0;
  680.          return(KEY_RESIZE);
  681.      } else {
  682.          ready_for_winch = 1;
  683.      }
  684. #endif /* RESIZING */
  685.  
  686.      if(time_out > 0) {
  687.          /* Check to see if there's bytes to read with a timeout */
  688. #ifdef USE_POLL
  689.      pollfd.fd = STDIN_FD;
  690.      pollfd.events = POLLIN;
  691.      dprint(9,(debugfile,"poll event %d, timeout %d\n", pollfd.events,
  692.            time_out));
  693.      res = poll (&pollfd, 1, time_out * 1000);
  694.      dprint(9, (debugfile, "poll on tty returned %d, events %d\n",
  695.             res, pollfd.revents));
  696. #else
  697.      FD_ZERO(&readfds);
  698.      FD_ZERO(&errfds);
  699.      FD_SET(STDIN_FD, &readfds);
  700.      FD_SET(STDIN_FD, &errfds);
  701.      tmo.tv_sec  = time_out;
  702.          tmo.tv_usec = 0; 
  703.  
  704.          dprint(9, (debugfile,"Select readfds:%d timeval:%d,%d\n", readfds,
  705.            tmo.tv_sec,tmo.tv_usec));
  706.  
  707.          res = select(STDIN_FD+1, &readfds, 0, &errfds, &tmo);
  708.  
  709.          dprint(9, (debugfile, "Select on tty returned %d\n", res));
  710. #endif
  711.  
  712.          if(res < 0) {
  713.              if(errno == EINTR) {
  714. #ifdef RESIZING
  715.                  ready_for_winch = 0;
  716. #endif
  717.                  return(NO_OP_COMMAND);
  718.              }
  719.              panic1("Select error: %s\n", error_description(errno));
  720.          }
  721.  
  722.          if(res == 0) { /* the select timed out */
  723.          if(getppid() == 1){
  724.          dprint(1, (debugfile,
  725.                 "\n\n** Parent found to be init!?!\n\n"));
  726.          read_bail();
  727.          /* NO RETURN */
  728.          }
  729.            
  730. #ifdef RESIZING
  731.              ready_for_winch = 0;
  732. #endif
  733.              return(time_out > 25 ? NO_OP_IDLE: NO_OP_COMMAND);
  734.          }
  735.      }
  736.  
  737. #ifdef RESIZING
  738.      ready_for_winch = 0;
  739. #endif
  740.  
  741.      return(READY_TO_READ);
  742. }
  743.  
  744.  
  745. /*----------------------------------------------------------------------
  746.      Lowest level read command. This reads one character.    (UNIX)
  747.  
  748.   Result: Returns the single character read or a NO_OP_COMMAND if the
  749.           read was interrupted.
  750.   ----*/
  751. int
  752. read_a_char()
  753. {
  754.      int        res;
  755.      unsigned char  c;
  756.  
  757.      res = read(STDIN_FD, &c, 1);
  758.  
  759.      if(res <= 0) {
  760.      /*
  761.       * Error reading from terminal!
  762.       * The only acceptable failure is being interrupted.  If so,
  763.       * return a value indicating such...
  764.       */
  765.      if(res < 0 && errno == EINTR){
  766.          dprint(2, (debugfile, "read_a_char interrupted\n"));
  767.          RETURN_CH(NO_OP_COMMAND);
  768.      }
  769.  
  770.      /*
  771.       * Else we read EOF or otherwise encountered some catastrophic
  772.       * error.  Treat it like SIGHUP (i.e., clean up and exit).
  773.       */
  774.      dprint(1, (debugfile, "\n\n** Error reading from tty : %s\n\n",
  775.             (res == 0) ? "EOF" : error_description(errno)));
  776.  
  777.      read_bail();
  778.      /* NO RETURN */
  779.      }
  780.  
  781.      dprint(9, (debugfile, "read_a_char return '\\%03o'\n", (int)c));
  782.      RETURN_CH((int)c);
  783. }
  784.  
  785.  
  786. /*----------------------------------------------------------------------
  787.      Simple version of read_a_char without the protection against
  788.      errors found there.  That's ok because kbseq will always return
  789.      after only a few characters and the rare error will be noticed
  790.      on the next call to read_a_char.
  791.  
  792.   Result: Returns the single character read or a NO_OP_COMMAND on error.
  793.   ----*/
  794. int
  795. getchar_for_kbseq()
  796. {
  797.     unsigned char c;
  798.  
  799.     if(read(STDIN_FD, &c, 1) <= 0)
  800.       RETURN_CH(NO_OP_COMMAND);
  801.     else
  802.       RETURN_CH((int)c);
  803. }
  804.  
  805.  
  806. /*----------------------------------------------------------------------
  807.   Read input characters with lots of processing for arrow keys and such  (UNIX)
  808.  
  809.  Args:  time_out -- The timeout to for the reads 
  810.  
  811.  Result: returns the character read. Possible special chars.
  812.  
  813.     This deals with function and arrow keys as well. 
  814.  
  815.   The idea is that this routine handles all escape codes so it done in
  816.   only one place. Especially so the back arrow key can work when entering
  817.   things on a line. Also so all function keys can be disabled and not
  818.   cause weird things to happen.
  819.   ---*/
  820. int
  821. read_char(time_out)
  822.     int time_out;
  823. {
  824.     int ch, status, cc;
  825.  
  826.     /* Get input from initial-keystrokes */
  827.     if(process_config_input(&ch))
  828.       return(ch);
  829.  
  830.     /*
  831.      * We only check for timeouts at the start of read_char, not in the
  832.      * middle of escape sequences.
  833.      */
  834.     ch = check_for_timeout(time_out);
  835.     if(ch == NO_OP_IDLE || ch == NO_OP_COMMAND || ch == KEY_RESIZE)
  836.       goto done;
  837.  
  838.     switch(status = kbseq(ps_global->kbesc, getchar_for_kbseq, &ch)){
  839.       case KEY_DOUBLE_ESC:
  840.     /*
  841.      * Special hack to get around comm devices eating control characters.
  842.      */
  843.     ch = read_a_char();
  844.     if(ch == NO_OP_COMMAND)        /* user typed ESC ESC, then stopped */
  845.       goto done;
  846.  
  847.     ch &= 0x7f;
  848.     if(isdigit(ch)){
  849.         int n = 0, i = ch - '0';
  850.  
  851.         if(!strchr("012", ch))
  852.           goto done;        /* bogus literal char value */
  853.  
  854.         while(n++ < 2){
  855.         ch = read_a_char();
  856.         if(ch == NO_OP_COMMAND || !isdigit(ch &= 0x7f)
  857.            || (n == 1 && i == 2 && ch > '5'))
  858.           goto done;        /* bogus literal char value */
  859.                     /* remember Horner? */
  860.         i = (i * 10) + (ch - '0');
  861.         }
  862.  
  863.         ch = i;
  864.     }
  865.     else{
  866.         if(islower(ch))        /* canonicalize if alpha */
  867.           ch = toupper(ch);
  868.  
  869.         ch = (isalpha(ch) || ch == '@' || (ch >= '[' && ch <= '_'))
  870.            ? ctrl(ch) : ch;
  871.     }
  872.  
  873.     goto done;
  874.  
  875. #ifdef MOUSE
  876.       case KEY_XTERM_MOUSE:
  877.     if(mouseexist()){
  878.         /*
  879.          * special hack to get mouse events from an xterm.
  880.          * Get the details, then pass it past the keymenu event
  881.          * handler, and then to the installed handler if there
  882.          * is one...
  883.          */
  884.         static int down = 0;
  885.         int        button, x, y;
  886.         unsigned   cmd;
  887.  
  888.         clear_cursor_pos();
  889.         ch = read_a_char();
  890.         if(ch == NO_OP_COMMAND)
  891.           goto done;
  892.  
  893.         button = ch & 0x03;
  894.  
  895.         ch = read_a_char();
  896.         if(ch == NO_OP_COMMAND)
  897.           goto done;
  898.  
  899.         x = ch - '!';
  900.  
  901.         ch = read_a_char();
  902.         if(ch == NO_OP_COMMAND)
  903.           goto done;
  904.  
  905.         y = ch - '!';
  906.  
  907.         ch = NO_OP_COMMAND;
  908.         if(button == 0){        /* xterm button 1 down */
  909.         down = 1;
  910.         if(checkmouse(&cmd, 1, x, y))
  911.           ch = (int)cmd;
  912.         }
  913.         else if (down && button == 3){
  914.         down = 0;
  915.         if(checkmouse(&cmd, 0, x, y))
  916.           ch = (int)cmd;
  917.         }
  918.  
  919.         goto done;
  920.     }
  921.  
  922.     break;
  923. #endif /* MOUSE */
  924.  
  925.       case  KEY_UP    :
  926.       case  KEY_DOWN    :
  927.       case  KEY_RIGHT    :
  928.       case  KEY_LEFT    :
  929.       case  KEY_PGUP    :
  930.       case  KEY_PGDN    :
  931.       case  KEY_HOME    :
  932.       case  KEY_END    :
  933.       case  KEY_DEL    :
  934.       case  PF1        :
  935.       case  PF2        :
  936.       case  PF3        :
  937.       case  PF4        :
  938.       case  PF5        :
  939.       case  PF6        :
  940.       case  PF7        :
  941.       case  PF8        :
  942.       case  PF9        :
  943.       case  PF10    :
  944.       case  PF11    :
  945.       case  PF12    :
  946.         dprint(9, (debugfile, "Read char returning: %d %s\n",
  947.                    status, pretty_command(status)));
  948.     return(status);
  949.  
  950.       case KEY_SWALLOW_Z:
  951.     status = KEY_JUNK;
  952.       case KEY_SWAL_UP:
  953.       case KEY_SWAL_DOWN:
  954.       case KEY_SWAL_LEFT:
  955.       case KEY_SWAL_RIGHT:
  956.     for(cc = 0; cc != 'z' && cc != '~';)
  957.       cc = read_a_char() & 0x7f;
  958.  
  959.         dprint(9, (debugfile, "Read char returning: %d %s\n",
  960.                    status, pretty_command(status)));
  961.     return(status);
  962.  
  963.       case KEY_KERMIT:
  964.     for(cc = 0; cc != '\033' || (read_a_char() & 0x7f) != '\\';)
  965.       cc = read_a_char() & 0x7f;
  966.  
  967.     ch = KEY_JUNK;
  968.     goto done;
  969.  
  970.       case BADESC:
  971.     ch = KEY_JUNK;
  972.     goto done;
  973.  
  974.       case 0:     /* regular character */
  975.       default:
  976.     /*
  977.      * we used to strip (ch &= 0x7f;), but this seems much cleaner
  978.      * in the face of line noise and has the benefit of making it
  979.      * tougher to emit mistakenly labeled MIME...
  980.      */
  981.     if((ch & 0x80) && (!ps_global->VAR_CHAR_SET
  982.                || !strucmp(ps_global->VAR_CHAR_SET, "US-ASCII"))){
  983.         dprint(9, (debugfile, "Read char returning: %d %s\n",
  984.                status, pretty_command(status)));
  985.         return(KEY_JUNK);
  986.     }
  987.     else if(ch == ctrl('Z')){
  988.         dprint(9, (debugfile, "Read char calling do_suspend\n"));
  989.         return(do_suspend(ps_global));
  990.     }
  991.  
  992.  
  993.       done:
  994.         dprint(9, (debugfile, "Read char returning: %d %s\n",
  995.                    ch, pretty_command(ch)));
  996.         return(ch);
  997.     }
  998. }
  999.  
  1000.  
  1001. /*----------------------------------------------------------------------
  1002.   Reading input somehow failed and we need to shutdown now
  1003.  
  1004.  Args:  none
  1005.  
  1006.  Result: pine exits
  1007.  
  1008.   ---*/
  1009. void
  1010. read_bail()
  1011. {
  1012.     end_signals(1);
  1013.     if(ps_global->inbox_stream){
  1014.     if(ps_global->inbox_stream == ps_global->mail_stream)
  1015.       ps_global->mail_stream = NULL;
  1016.  
  1017.     if(!ps_global->inbox_stream->lock)        /* shouldn't be... */
  1018.       mail_close(ps_global->inbox_stream);
  1019.     }
  1020.  
  1021.     if(ps_global->mail_stream && !ps_global->mail_stream->lock)
  1022.       mail_close(ps_global->mail_stream);
  1023.  
  1024.     end_keyboard(F_ON(F_USE_FK,ps_global));
  1025.     end_tty_driver(ps_global);
  1026.     if(filter_data_file(0))
  1027.       unlink(filter_data_file(0));
  1028.  
  1029.     exit(0);
  1030. }
  1031.  
  1032.  
  1033. extern char term_name[]; /* term_name from ttyout.c-- affect keyboard*/
  1034. /* -------------------------------------------------------------------
  1035.      Set up the keyboard -- usually enable some function keys  (UNIX)
  1036.  
  1037.     Args: struct pine 
  1038.  
  1039. So far all we do here is turn on keypad mode for certain terminals
  1040.  
  1041. Hack for NCSA telnet on an IBM PC to put the keypad in the right mode.
  1042. This is the same for a vtXXX terminal or [zh][12]9's which we have 
  1043. a lot of at UW
  1044.   ----*/
  1045. void
  1046. init_keyboard(use_fkeys)
  1047.      int use_fkeys;
  1048. {
  1049.     if(use_fkeys && (!strucmp(term_name,"vt102")
  1050.              || !strucmp(term_name,"vt100")))
  1051.       printf("\033\133\071\071\150");
  1052. }
  1053.  
  1054.  
  1055.  
  1056. /*----------------------------------------------------------------------
  1057.      Clear keyboard, usually disable some function keys           (UNIX)
  1058.  
  1059.    Args:  pine state (terminal type)
  1060.  
  1061.  Result: keyboard state reset
  1062.   ----*/
  1063. void
  1064. end_keyboard(use_fkeys)
  1065.      int use_fkeys;
  1066. {
  1067.     if(use_fkeys && (!strcmp(term_name, "vt102")
  1068.              || !strcmp(term_name, "vt100"))){
  1069.     printf("\033\133\071\071\154");
  1070.     fflush(stdout);
  1071.     }
  1072. }
  1073.  
  1074.     
  1075. /*----------------------------------------------------------------------
  1076.    Discard any pending input characters                (UNIX)
  1077.  
  1078.    Args:  none
  1079.  
  1080.  Result: pending input buffer flushed
  1081.   ----*/
  1082. void
  1083. flush_input()
  1084. {
  1085. #ifdef    HAVE_TERMIOS
  1086.     tcflush(STDIN_FD, TCIFLUSH);
  1087. #else
  1088. #ifdef    HAVE_TERMIO
  1089.     ioctl(STDIN_FD, TCFLSH, 0);
  1090. #else
  1091. #ifdef    TIOCFLUSH
  1092. #ifdef    FREAD
  1093.     int i = FREAD;
  1094. #else
  1095.     int i = 1;
  1096. #endif
  1097.  
  1098.     ioctl(STDIN_FD, TIOCFLUSH, &i);
  1099. #else
  1100. #endif    /* TIOCFLUSH */
  1101. #endif    /* HAVE_TERMIO */
  1102. #endif    /* HAVE_TERMIOS */
  1103. }
  1104.  
  1105.     
  1106. #else
  1107. /*
  1108.  * DOS && OS/2 specific code.
  1109.  * Middle of giant switch between UNIX and DOS/OS2 input drivers
  1110.  */
  1111.  
  1112.  
  1113. #ifdef OS2
  1114. #define INCL_BASE
  1115. #define INCL_DOS
  1116. #define INCL_VIO
  1117. #define INCL_KBD
  1118. #define INCL_NOPM
  1119. #include <os2.h>
  1120. #undef ADDRESS
  1121. #endif
  1122.  
  1123. #include "headers.h"
  1124.  
  1125.  
  1126. /*
  1127.  * Internal prototypes
  1128.  */
  1129. void line_paint PROTO((int, int *));
  1130. int  process_config_input PROTO((int *));
  1131.  
  1132.  
  1133.  
  1134. #if    !(defined(WIN32) || defined(OS2))
  1135. #include <dos.h>
  1136. #include <bios.h>
  1137. #endif
  1138.  
  1139. /* global to tell us if we have an enhanced keyboard! */
  1140. static int enhanced = 0;
  1141. /* global function to execute while waiting for character input */
  1142. void   (*while_waiting)() = NULL;
  1143.  
  1144. #ifdef _WINDOWS
  1145. /* global to tell us if the window was resized. */
  1146. static int DidResize = FALSE;
  1147.  
  1148.  
  1149. /*----------------------------------------------------------------------
  1150.     Flag the fact the window has resized.
  1151. */
  1152. int
  1153. pine_window_resize_callback ()
  1154. {
  1155.     DidResize = TRUE;
  1156. }
  1157. #endif
  1158.  
  1159.  
  1160.  
  1161. /*----------------------------------------------------------------------
  1162.     Initialize the tty driver to do single char I/O and whatever else  (DOS)
  1163.  
  1164.  Input:  struct pine
  1165.  
  1166.  Result: tty driver is put in raw mode
  1167.   ----------------------------------------------------------------------*/
  1168. init_tty_driver(pine)
  1169.      struct pine *pine;
  1170. {
  1171. #ifdef _WINDOWS
  1172.     mswin_setresizecallback (pine_window_resize_callback);
  1173.     init_mouse ();            /* always a mouse under windows? */
  1174. #else
  1175. #ifdef OS2
  1176.     enhanced = 1;
  1177. #else
  1178.     /* detect enhanced keyboard */
  1179.     enhanced = enhanced_keybrd();    /* are there extra keys? */
  1180. #endif
  1181. #endif
  1182.     pine = pine;            /* Get rid of unused parm warning */
  1183.     return(Raw(1));
  1184. }
  1185.  
  1186.  
  1187.  
  1188. /*----------------------------------------------------------------------
  1189.        End use of the tty, put it back into it's normal mode          (DOS)
  1190.  
  1191.  Input:  struct pine
  1192.  
  1193.  Result: tty driver mode change
  1194.   ----------------------------------------------------------------------*/
  1195. void
  1196. end_tty_driver(pine)
  1197.      struct pine *pine;
  1198. {
  1199.     dprint(2, (debugfile, "about to end_tty_driver\n"));
  1200. #ifdef _WINDOWS
  1201.     mswin_clearresizecallback (pine_window_resize_callback);
  1202. #endif
  1203. }
  1204.  
  1205. /*----------------------------------------------------------------------
  1206.    translate IBM Keyboard Extended Functions to things pine understands.
  1207.    More work can be done to make things like Home, PageUp and PageDown work. 
  1208.  
  1209. /*
  1210.  * extended_code - return special key definition
  1211.  */
  1212. extended_code(kc)
  1213. unsigned  kc;
  1214. {
  1215.     switch(kc){
  1216. #ifdef    _WINDOWS
  1217.     case MSWIN_KEY_F1: return(PF1);
  1218.     case MSWIN_KEY_F2: return(PF2);
  1219.     case MSWIN_KEY_F3: return(PF3);
  1220.     case MSWIN_KEY_F4: return(PF4);
  1221.     case MSWIN_KEY_F5: return(PF5);
  1222.     case MSWIN_KEY_F6: return(PF6);
  1223.     case MSWIN_KEY_F7: return(PF7);
  1224.     case MSWIN_KEY_F8: return(PF8);
  1225.     case MSWIN_KEY_F9: return(PF9);
  1226.     case MSWIN_KEY_F10: return(PF10);
  1227.     case MSWIN_KEY_F11: return(PF11);
  1228.     case MSWIN_KEY_F12: return(PF12);
  1229.  
  1230.     case MSWIN_KEY_UP: return(KEY_UP);
  1231.     case MSWIN_KEY_DOWN: return(KEY_DOWN);
  1232.     case MSWIN_KEY_LEFT: return(KEY_LEFT);
  1233.     case MSWIN_KEY_RIGHT: return(KEY_RIGHT);
  1234.     case MSWIN_KEY_HOME: return(KEY_HOME);
  1235.     case MSWIN_KEY_END: return(KEY_END);
  1236.     case MSWIN_KEY_SCROLLUPPAGE:
  1237.     case MSWIN_KEY_PREVPAGE: return(KEY_PGUP);
  1238.     case MSWIN_KEY_SCROLLDOWNPAGE:
  1239.     case MSWIN_KEY_NEXTPAGE: return(KEY_PGDN);
  1240.     case MSWIN_KEY_DELETE: return(KEY_DEL);
  1241.     case MSWIN_KEY_SCROLLUPLINE: return (KEY_SCRLUPL);
  1242.     case MSWIN_KEY_SCROLLDOWNLINE: return (KEY_SCRLDNL);
  1243.     case MSWIN_KEY_SCROLLTO: return (KEY_SCRLTO);
  1244.  
  1245.     case MSWIN_KEY_NODATA:    return (NO_OP_COMMAND);
  1246. #else
  1247.     case 0x3b00 : return(PF1);
  1248.     case 0x3c00 : return(PF2);
  1249.     case 0x3d00 : return(PF3);
  1250.     case 0x3e00 : return(PF4);
  1251.     case 0x3f00 : return(PF5);
  1252.     case 0x4000 : return(PF6);
  1253.     case 0x4100 : return(PF7);
  1254.     case 0x4200 : return(PF8);
  1255.     case 0x4300 : return(PF9);
  1256.     case 0x4400 : return(PF10);
  1257.     case 0x8500 : return(PF11);
  1258.     case 0x8600 : return(PF12);
  1259.  
  1260.     case 0x4800 : return(KEY_UP);
  1261.     case 0x5000 : return(KEY_DOWN);
  1262.     case 0x4b00 : return(KEY_LEFT);
  1263.     case 0x4d00 : return(KEY_RIGHT);
  1264.     case 0x4700 : return(KEY_HOME);
  1265.     case 0x4f00 : return(KEY_END);
  1266.     case 0x4900 : return(KEY_PGUP);
  1267.     case 0x5100 : return(KEY_PGDN);
  1268.     case 0x5300 : return(KEY_DEL);
  1269.     case 0x48e0 : return(KEY_UP);            /* grey key version */
  1270.     case 0x50e0 : return(KEY_DOWN);            /* grey key version */
  1271.     case 0x4be0 : return(KEY_LEFT);            /* grey key version */
  1272.     case 0x4de0 : return(KEY_RIGHT);        /* grey key version */
  1273.     case 0x47e0 : return(KEY_HOME);            /* grey key version */
  1274.     case 0x4fe0 : return(KEY_END);            /* grey key version */
  1275.     case 0x49e0 : return(KEY_PGUP);            /* grey key version */
  1276.     case 0x51e0 : return(KEY_PGDN);            /* grey key version */
  1277.     case 0x53e0 : return(KEY_DEL);            /* grey key version */
  1278. #endif
  1279.     default   : return(NO_OP_COMMAND);
  1280.     }
  1281. }
  1282.  
  1283.  
  1284.  
  1285. /*----------------------------------------------------------------------
  1286.    Read input characters with lots of processing for arrow keys and such (DOS)
  1287.  
  1288.  Input:  none
  1289.  
  1290.  Result: returns the character read. Possible special chars defined h file
  1291.  
  1292.  
  1293.     This deals with function and arrow keys as well. 
  1294.   It returns ^T for up , ^U for down, ^V for forward and ^W for back.
  1295.   These are just sort of arbitrarily picked and might be changed.
  1296.   They are defined in defs.h. Didn't want to use 8 bit chars because
  1297.   the values are signed chars, though it ought to work with negative 
  1298.   values. 
  1299.  
  1300.   The idea is that this routine handles all escape codes so it done in
  1301.   only one place. Especially so the back arrow key can work when entering
  1302.   things on a line. Also so all function keys can be broken and not
  1303.   cause weird things to happen.
  1304. ----------------------------------------------------------------------*/
  1305.  
  1306. int
  1307. read_char(tm)
  1308. int tm;
  1309. {
  1310.     unsigned   ch = 0;
  1311.     long       timein;
  1312. #ifndef    _WINDOWS
  1313.     unsigned   intrupt = 0;
  1314.     extern int win_multiplex();
  1315. #endif
  1316.  
  1317.     if(process_config_input((int *) &ch))
  1318.       RETURN_CH(ch);
  1319.  
  1320. #ifdef _WINDOWS
  1321.     if (DidResize) {
  1322.      DidResize = FALSE;
  1323.      RETURN_CH (get_windsize (ps_global->ttyo));
  1324.     }
  1325. #endif
  1326.  
  1327. #ifdef OS2
  1328.     vidUpdate();
  1329. #endif
  1330. #ifdef MOUSE
  1331.     mouseon();
  1332. #endif
  1333.  
  1334.     if(tm){
  1335.     timein = time(0L);
  1336. #ifdef _WINDOWS
  1337.     /* mswin_charavail() Yeilds control to other window apps. */
  1338.     while (!mswin_charavail()) {
  1339. #else
  1340. #ifdef OS2
  1341.     while (!kbd_ready()) {
  1342. #else
  1343.     while(!_bios_keybrd(enhanced ? _NKEYBRD_READY : _KEYBRD_READY)){
  1344. #endif
  1345. #endif
  1346.         if(time(0L) >= timein+tm){
  1347.         ch = NO_OP_COMMAND;
  1348.         goto gotone;
  1349.         }
  1350. #ifdef _WINDOWS
  1351.         if (DidResize) {
  1352.         DidResize = FALSE;
  1353.         RETURN_CH( get_windsize (ps_global->ttyo));
  1354.         }
  1355. #endif
  1356. #ifdef    MOUSE
  1357.          if(checkmouse(&ch,0,0,0))
  1358.           goto gotone;
  1359. #endif
  1360.         if(while_waiting)
  1361.           (*while_waiting)();
  1362.  
  1363. #ifndef    _WINDOWS
  1364.         /*
  1365.          * the number "30" was not reached via experimentation
  1366.          * or scientific analysis of any kind.
  1367.          */
  1368.         if(((intrupt++) % 30) == 0)    /* surrender CPU to windows */
  1369.           win_multiplex();
  1370. #endif
  1371.     }
  1372.     }
  1373.  
  1374. #ifdef _WINDOWS
  1375.     ch = mswin_getc_fast();
  1376. #else
  1377. #ifdef OS2
  1378.     ch = kbd_getkey();
  1379. #else
  1380.     ch = _bios_keybrd(enhanced ? _NKEYBRD_READ : _KEYBRD_READ);
  1381. #endif
  1382. #endif
  1383.  
  1384. gotone:
  1385. #if defined(MOUSE)
  1386.     mouseoff();
  1387.  
  1388.     /* More obtuse key mapping.  If it is a mouse event, the return
  1389.      * may be KEY_MOUSE, which indicates to the upper layer that it
  1390.      * is a mouse event.  Return it here to avoid the code that
  1391.      * follows which would do a (ch & 0xff).
  1392.      */
  1393.     if (ch == KEY_MOUSE)
  1394.       RETURN_CH(ch);
  1395. #endif
  1396.  
  1397.     /*
  1398.      * WARNING: Hack notice.
  1399.      * the mouse interaction complicates this expression a bit as 
  1400.      * if function key mode is set, PFn values are setup for return
  1401.      * by the mouse event catcher.  For now, just special case them
  1402.      * since they don't conflict with any of the DOS special keys.
  1403.      */
  1404. #ifdef _WINDOWS
  1405.  
  1406.     if (ch >= MSWIN_RANGE_START && ch <= MSWIN_RANGE_END)
  1407.         RETURN_CH (extended_code (ch));
  1408.  
  1409.     RETURN_CH (ch&0xff);
  1410. #else /* DOS */
  1411.     if((ch & 0xff) == ctrl('Z'))
  1412.       RETURN_CH(do_suspend(ps_global));
  1413.  
  1414.     RETURN_CH((ch >= PF1 && ch <= PF12)
  1415.            ? ch
  1416.            : ((ch&0xff) && ((ch&0xff) != 0xe0))
  1417.           ? (ch&0xff)
  1418.           : extended_code(ch));
  1419. #endif
  1420. }
  1421.  
  1422. #ifdef OS2
  1423. static KBDINFO initialKbdInfo;
  1424. #endif
  1425.  
  1426. /* -------------------------------------------------------------------
  1427.      Set up the keyboard -- usually enable some function keys     (DOS)
  1428.  
  1429.   Input: struct pine (terminal type)
  1430.  
  1431.   Result: keyboard set up
  1432.  
  1433. -----------------------------------------------------------------------*/
  1434. void
  1435. init_keyboard(use_fkeys)
  1436.      int use_fkeys;
  1437. {
  1438. #ifdef OS2
  1439.   KBDINFO kbdInfo;
  1440.   KbdGetStatus(&initialKbdInfo, 0);
  1441.   kbdInfo = initialKbdInfo;
  1442.   kbdInfo.fsMask &= ~(0x0001|0x0008|0x0100); /* echo cooked off */
  1443.   kbdInfo.fsMask |= (0x002|0x004|0x0100); /* noecho,raw,shiftrpt on */
  1444.   KbdSetStatus(&kbdInfo, 0);
  1445. #endif
  1446. }
  1447.  
  1448.  
  1449.  
  1450. /*----------------------------------------------------------------------
  1451.      Clear keyboard, usually disable some function keys            (DOS)
  1452.  
  1453.  Input:  pine state (terminal type)
  1454.  
  1455.  Result: keyboard state reset
  1456.   ----------------------------------------------------------------------*/
  1457. /* BUG shouldn't have to check for pine != NULL */
  1458. void
  1459. end_keyboard(use_fkeys)
  1460.      int use_fkeys;
  1461. {
  1462. #ifdef OS2
  1463.   KbdSetStatus(&initialKbdInfo, 0);
  1464. #endif
  1465. }
  1466.  
  1467.  
  1468.  
  1469. /*----------------------------------------------------------------------
  1470.    Discard any pending input characters                (DOS)
  1471.  
  1472.    Args:  none
  1473.  
  1474.  Result: pending input buffer flushed
  1475.   ----*/
  1476. void
  1477. flush_input()
  1478. {
  1479. #ifdef _WINDOWS
  1480.     while (mswin_charavail ())
  1481.         (void) mswin_getc ();
  1482. #else
  1483. #ifdef OS2
  1484.     kbd_flush();
  1485. #else
  1486.     while(_bios_keybrd(enhanced ? _NKEYBRD_READY : _KEYBRD_READY))
  1487.       (void) _bios_keybrd(enhanced ? _NKEYBRD_READ : _KEYBRD_READ);
  1488. #endif
  1489. #endif
  1490. }
  1491.  
  1492.     
  1493. /*----------------------------------------------------------------------
  1494.     Actually set up the tty driver                             (DOS)
  1495.  
  1496.    Args: state -- which state to put it in. 1 means go into raw, 0 out of
  1497.  
  1498.   Result: returns 0 if successful and -1 if not.
  1499.   ----*/
  1500.  
  1501. Raw(state)
  1502. int state;
  1503. {
  1504. /* of course, DOS never runs at low speed!!! */
  1505.     ps_global->low_speed = 0;
  1506.     return(0);
  1507. }
  1508.  
  1509.  
  1510. /*----------------------------------------------------------------------
  1511.     Set up the tty driver to use XON/XOFF flow control        (DOS)
  1512.  
  1513.    Args: state -- True to make sure XON/XOFF turned on, FALSE default state
  1514.  
  1515.   Result: none.
  1516.   ----*/
  1517. void
  1518. xonxoff_proc(state)
  1519.     int state;
  1520. {
  1521.     return;                    /* no op */
  1522. }
  1523.  
  1524.  
  1525. /*----------------------------------------------------------------------
  1526.     Set up the tty driver to do LF->CR translation        (DOS)
  1527.  
  1528.    Args: state -- True to turn on translation, false to write raw LF's
  1529.  
  1530.   Result: none.
  1531.  
  1532.   ----*/
  1533. void
  1534. crlf_proc(state)
  1535.     int state;
  1536. {
  1537.     return;                    /* no op */
  1538. }
  1539.  
  1540.  
  1541. /*----------------------------------------------------------------------
  1542.     Set up the tty driver to hanle interrupt char        (DOS)
  1543.  
  1544.    Args: state -- True to turn on interrupt char, false to not
  1545.  
  1546.   Result: tty driver that'll send us SIGINT or not
  1547.  
  1548.   ----*/
  1549. void
  1550. intr_proc(state)
  1551.     int state;
  1552. {
  1553.     return;                    /* no op */
  1554. }
  1555. #endif /* DOS End of giant switch between UNX and DOS input drivers */
  1556.  
  1557.  
  1558. /*----------------------------------------------------------------------
  1559.         Read a character from keyboard with timeout
  1560.  Input:  none
  1561.  
  1562.  Result: Returns command read via read_char
  1563.          Times out and returns a null command every so often
  1564.  
  1565.   Calculates the timeout for the read, and does a few other house keeping 
  1566. things.  The duration of the timeout is set in pine.c.
  1567.   ----------------------------------------------------------------------*/
  1568. int
  1569. read_command()
  1570. {
  1571.     int ch, tm = 0;
  1572.     long dtime; 
  1573.  
  1574.     cancel_busy_alarm(-1);
  1575.     tm = (messages_queued(&dtime) > 1) ? (int)dtime : timeout;
  1576.  
  1577.     ch = read_char(tm);
  1578.     dprint(9, (debugfile, "Read command returning: %d %s\n", ch,
  1579.               pretty_command(ch)));
  1580.     if(ch != NO_OP_COMMAND && ch != NO_OP_IDLE && ch != KEY_RESIZE)
  1581.       zero_new_mail_count();
  1582.  
  1583.     return(ch);
  1584. }
  1585.  
  1586.  
  1587.  
  1588.  
  1589. /*
  1590.  *
  1591.  */
  1592. static struct display_line {
  1593.     int   row, col;            /* where display starts         */
  1594.     int   dlen;                /* length of display line     */
  1595.     char *dl;                /* line on display         */
  1596.     char *vl;                /* virtual line          */
  1597.     int   vlen;                /* length of virtual line        */
  1598.     int   vused;            /* length of virtual line in use */
  1599.     int   vbase;            /* first virtual char on display */
  1600. } dline;
  1601.  
  1602.  
  1603.  
  1604. static struct key oe_keys[] =
  1605.        {{"^G","Help",KS_SCREENHELP},    {"^C","Cancel",KS_NONE},
  1606.     {"^T","xxx",KS_NONE},        {"Ret","Accept",KS_NONE},
  1607.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE},
  1608.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE},
  1609.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE},
  1610.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE}};
  1611. static struct key_menu oe_keymenu =
  1612.     {sizeof(oe_keys)/(sizeof(oe_keys[0])*12), 0, 0,0,0,0, oe_keys};
  1613. #define    OE_HELP_KEY    0
  1614. #define    OE_CANCEL_KEY    1
  1615. #define    OE_CTRL_T_KEY    2
  1616. #define    OE_ENTER_KEY    3
  1617.  
  1618.  
  1619. /*---------------------------------------------------------------------- 
  1620.        Prompt user for a string in status line with various options
  1621.  
  1622.   Args: string -- the buffer result is returned in, and original string (if 
  1623.                    any) is passed in.
  1624.         y_base -- y position on screen to start on. 0,0 is upper left
  1625.                     negative numbers start from bottom
  1626.         x_base -- column position on screen to start on. 0,0 is upper left
  1627.         field_len -- Maximum length of string to accept
  1628.         append_current -- flag indicating string should not be truncated before
  1629.                           accepting input
  1630.         passwd -- a pass word is being fetch. Don't echo on screen
  1631.         prompt -- The string to prompt with
  1632.     escape_list -- pointer to array of ESCKEY_S's.  input chars matching
  1633.                        those in list return value from list.
  1634.         help   -- Arrary of strings for help text in bottom screen lines
  1635.         flags  -- flags
  1636.  
  1637.   Result:  editing input string
  1638.             returns -1 unexpected errors
  1639.             returns 0  normal entry typed (editing and return or PF2)
  1640.             returns 1  typed ^C or PF2 (cancel)
  1641.             returns 3  typed ^G or PF1 (help)
  1642.             returns 4  typed ^L for a screen redraw
  1643.  
  1644.   WARNING: Care is required with regard to the escape_list processing.
  1645.            The passed array is terminated with an entry that has ch = -1.
  1646.            Function key labels and key strokes need to be setup externally!
  1647.        Traditionally, a return value of 2 is used for ^T escapes.
  1648.  
  1649.    Unless in escape_list, tabs are trapped by isprint().
  1650. This allows near full weemacs style editing in the line
  1651.    ^A beginning of line
  1652.    ^E End of line
  1653.    ^R Redraw line
  1654.    ^G Help
  1655.    ^F forward
  1656.    ^B backward
  1657.    ^D delete
  1658. ----------------------------------------------------------------------*/
  1659.  
  1660. optionally_enter(string, y_base, x_base, field_len, append_current, passwd,
  1661.                  prompt, escape_list, help, flags)
  1662.      char       *string, *prompt;
  1663.      ESCKEY_S   *escape_list;
  1664.      HelpType     help;
  1665.      int         x_base, y_base, field_len, append_current, passwd;
  1666.      unsigned     flags;
  1667. {
  1668.     register char *s2;
  1669.     register int   field_pos;
  1670.     int            i, j, return_v, cols, ch, prompt_len, too_thin,
  1671.                    real_y_base, cursor_moved, km_popped;
  1672.     char          *saved_original = NULL, *k, *kb;
  1673.     char          *kill_buffer = NULL;
  1674.     char         **help_text;
  1675.     int           fkey_table[12];
  1676.     struct       key_menu *km;
  1677.     bitmap_t       bitmap;
  1678.  
  1679.     dprint(5, (debugfile, "=== optionally_enter called ===\n"));
  1680.     dprint(9, (debugfile, "string:\"%s\"  y:%d  x:%d  length: %d append: %d\n",
  1681.                string, x_base, y_base, field_len, append_current));
  1682.     dprint(9, (debugfile, "passwd:%d   prompt:\"%s\"   label:\"%s\"\n",
  1683.                passwd, prompt, (escape_list && escape_list[0].ch != -1)
  1684.                  ? escape_list[0].label: ""));
  1685.  
  1686. #ifdef _WINDOWS
  1687.     if (mswin_usedialog ()) {
  1688.     MDlgButton        button_list[12];
  1689.     int            b;
  1690.     int            i;
  1691.  
  1692.     memset (&button_list, 0, sizeof (MDlgButton) * 12);
  1693.     b = 0;
  1694.     for (i = 0; escape_list && escape_list[i].ch != -1 && i < 11; ++i) {
  1695.         if (escape_list[i].name != NULL
  1696.         && escape_list[i].ch > 0 && escape_list[i].ch < 256) {
  1697.         button_list[b].ch = escape_list[i].ch;
  1698.         button_list[b].rval = escape_list[i].rval;
  1699.         button_list[b].name = escape_list[i].name;
  1700.         button_list[b].label = escape_list[i].label;
  1701.         ++b;
  1702.         }
  1703.     }
  1704.     button_list[b].ch = -1;
  1705.  
  1706.  
  1707.     help_text = get_help_text (help, NULL);
  1708.     return_v = mswin_dialog (prompt, string, field_len, 
  1709.             append_current, passwd, button_list, help_text, flags);
  1710.     if (help_text != NULL) 
  1711.         free_help_text (help_text);
  1712.         return (return_v);
  1713.     }
  1714. #endif
  1715.  
  1716.     suspend_busy_alarm();
  1717.     cols       = ps_global->ttyo->screen_cols;
  1718.     prompt_len = strlen(prompt);
  1719.     too_thin   = 0;
  1720.     km_popped  = 0;
  1721.     if(y_base > 0) {
  1722.         real_y_base = y_base;
  1723.     } else {
  1724.         real_y_base=  y_base + ps_global->ttyo->screen_rows;
  1725.         if(real_y_base < 2)
  1726.           real_y_base = ps_global->ttyo->screen_rows;
  1727.     }
  1728.  
  1729.     flush_ordered_messages();
  1730.     mark_status_dirty();
  1731.     if(append_current)            /* save a copy in case of cancel */
  1732.       saved_original = cpystr(string);
  1733.  
  1734.     /*
  1735.      * build the function key mapping table, skipping predefined keys...
  1736.      */
  1737.     memset(fkey_table, NO_OP_COMMAND, 12 * sizeof(int));
  1738.     for(i = 0, j = 0; escape_list && escape_list[i].ch != -1 && i+j < 12; i++){
  1739.     if(i+j == OE_HELP_KEY)
  1740.       j++;
  1741.  
  1742.     if(i+j == OE_CANCEL_KEY)
  1743.       j++;
  1744.  
  1745.     if(i+j == OE_ENTER_KEY)
  1746.       j++;
  1747.  
  1748.     fkey_table[i+j] = escape_list[i].ch;
  1749.     }
  1750.  
  1751. #if defined(HELPFILE)
  1752.     help_text = (help != NO_HELP) ? get_help_text(help, NULL) : (char **)NULL;
  1753. #else
  1754.     help_text = help;
  1755. #endif
  1756.     if(help_text){            /*---- Show help text -----*/
  1757.     int width = ps_global->ttyo->screen_cols - x_base;
  1758.  
  1759.     if(FOOTER_ROWS(ps_global) == 1){
  1760.         km_popped++;
  1761.         FOOTER_ROWS(ps_global) = 3;
  1762.         clearfooter(ps_global);
  1763.  
  1764.         y_base = -3;
  1765.         real_y_base = y_base + ps_global->ttyo->screen_rows;
  1766.     }
  1767.  
  1768.     for(j = 0; j < 2 && help_text[j]; j++){
  1769.         MoveCursor(real_y_base + 1 + j, x_base);
  1770.         CleartoEOLN();
  1771.  
  1772.         if(width < strlen(help_text[j])){
  1773.         char *tmp = fs_get((width + 1) * sizeof(char));
  1774.         strncpy(tmp, help_text[j], width);
  1775.         tmp[width] = '\0';
  1776.         PutLine0(real_y_base + 1 + j, x_base, tmp);
  1777.         fs_give((void **)&tmp);
  1778.         }
  1779.         else
  1780.           PutLine0(real_y_base + 1 + j, x_base, help_text[j]);
  1781.     }
  1782.  
  1783. #if defined(HELPFILE)
  1784.     free_help_text(help_text);
  1785. #endif
  1786.  
  1787.     } else {
  1788.     clrbitmap(bitmap);
  1789.     clrbitmap((km = &oe_keymenu)->bitmap);        /* force formatting */
  1790.     setbitn(OE_HELP_KEY, bitmap);
  1791.     setbitn(OE_ENTER_KEY, bitmap);
  1792.         if(!(flags & OE_DISALLOW_CANCEL))
  1793.         setbitn(OE_CANCEL_KEY, bitmap);
  1794.     setbitn(OE_CTRL_T_KEY, bitmap);
  1795.  
  1796.         /*---- Show the usual possible keys ----*/
  1797.     for(i=0,j=0; escape_list && escape_list[i].ch != -1 && i+j < 12; i++){
  1798.         if(i+j == OE_HELP_KEY)
  1799.           j++;
  1800.  
  1801.         if(i+j == OE_CANCEL_KEY)
  1802.           j++;
  1803.  
  1804.         if(i+j == OE_ENTER_KEY)
  1805.           j++;
  1806.  
  1807.         oe_keymenu.keys[i+j].label = escape_list[i].label;
  1808.         oe_keymenu.keys[i+j].name = escape_list[i].name;
  1809.         setbitn(i+j, bitmap);
  1810.     }
  1811.  
  1812.     for(i = i+j; i < 12; i++)
  1813.       if(!(i == OE_HELP_KEY || i == OE_ENTER_KEY || i == OE_CANCEL_KEY))
  1814.         oe_keymenu.keys[i].name = NULL;
  1815.  
  1816.     draw_keymenu(km, bitmap, cols, 1-FOOTER_ROWS(ps_global),
  1817.         0, FirstMenu, 0);
  1818.     }
  1819.     
  1820.     
  1821.     StartInverse();  /* Always in inverse  */
  1822.  
  1823.     /*
  1824.      * if display length isn't wide enough to support input,
  1825.      * shorten up the prompt...
  1826.      */
  1827.     if((dline.dlen = cols - (x_base + prompt_len + 1)) < 5){
  1828.     prompt_len += (dline.dlen - 5);    /* adding negative numbers */
  1829.     prompt     -= (dline.dlen - 5);    /* subtracting negative numbers */
  1830.     dline.dlen  = 5;
  1831.     }
  1832.  
  1833.     dline.dl    = fs_get((size_t)dline.dlen + 1);
  1834.     memset((void *)dline.dl, 0, (size_t)(dline.dlen + 1) * sizeof(char));
  1835.     dline.row   = real_y_base;
  1836.     dline.col   = x_base + prompt_len;
  1837.     dline.vl    = string;
  1838.     dline.vlen  = --field_len;        /* -1 for terminating NULL */
  1839.     dline.vbase = field_pos = 0;
  1840.  
  1841.     PutLine0(real_y_base, x_base, prompt);
  1842.     /* make sure passed in string is shorter than field_len */
  1843.     /* and adjust field_pos..                               */
  1844.  
  1845.     while(append_current && field_pos < field_len && string[field_pos] != '\0')
  1846.       field_pos++;
  1847.  
  1848.     string[field_pos] = '\0';
  1849.     dline.vused = (int)(&string[field_pos] - string);
  1850.     line_paint(field_pos, &passwd);
  1851.  
  1852. #ifdef    _WINDOWS
  1853.     mswin_allowpaste(MSWIN_PASTE_LINE);
  1854. #endif
  1855.  
  1856.     /*----------------------------------------------------------------------
  1857.       The main loop
  1858.    
  1859.     here field_pos is the position in the string.
  1860.     s always points to where we are in the string.
  1861.     loops until someone sets the return_v.
  1862.       ----------------------------------------------------------------------*/
  1863.     return_v = -10;
  1864.  
  1865.     while(return_v == -10) {
  1866.     /* Timeout 5 min to keep imap mail stream alive */
  1867.         ch = read_char(600);
  1868.  
  1869.     /*
  1870.      * Don't want to intercept all characters if typing in passwd.
  1871.      * We select an ad hoc set that we will catch and let the rest
  1872.      * through.  We would have caught the set below in the big switch
  1873.      * but we skip the switch instead.  Still catch things like ^K,
  1874.      * DELETE, ^C, RETURN.
  1875.      */
  1876.     if(passwd)
  1877.       switch(ch) {
  1878.             case ctrl('F'):  
  1879.         case KEY_RIGHT:
  1880.             case ctrl('B'):
  1881.         case KEY_LEFT:
  1882.             case ctrl('U'):
  1883.             case ctrl('A'):
  1884.         case KEY_HOME:
  1885.             case ctrl('E'):
  1886.         case KEY_END:
  1887.         case TAB:
  1888.           goto ok_for_passwd;
  1889.       }
  1890.  
  1891.         if(too_thin && ch != KEY_RESIZE && ch != ctrl('Z'))
  1892.           goto bleep;
  1893.  
  1894.     switch(ch) {
  1895.  
  1896.         /*--------------- KEY RIGHT ---------------*/
  1897.           case ctrl('F'):  
  1898.       case KEY_RIGHT:
  1899.         if(field_pos >= field_len || string[field_pos] == '\0')
  1900.               goto bleep;
  1901.  
  1902.         line_paint(++field_pos, &passwd);
  1903.         break;
  1904.  
  1905.         /*--------------- KEY LEFT ---------------*/
  1906.           case ctrl('B'):
  1907.       case KEY_LEFT:
  1908.         if(field_pos <= 0)
  1909.           goto bleep;
  1910.  
  1911.         line_paint(--field_pos, &passwd);
  1912.         break;
  1913.  
  1914.           /*--------------------  RETURN --------------------*/
  1915.       case PF4:
  1916.         if(F_OFF(F_USE_FK,ps_global)) goto bleep;
  1917.       case ctrl('J'): 
  1918.       case ctrl('M'): 
  1919.         return_v = 0;
  1920.         break;
  1921.  
  1922.           /*-------------------- Destructive backspace --------------------*/
  1923.       case '\177': /* DEL */
  1924.       case ctrl('H'):
  1925.             /*   Try and do this with by telling the terminal to delete a
  1926.                  a character. If that fails, then repaint the rest of the
  1927.                  line, acheiving the same much less efficiently
  1928.              */
  1929.         if(field_pos <= 0) goto bleep;
  1930.         field_pos--;
  1931.         /* drop thru to pull line back ... */
  1932.  
  1933.           /*-------------------- Delete char --------------------*/
  1934.       case ctrl('D'): 
  1935.       case KEY_DEL: 
  1936.             if(field_pos >= field_len || !string[field_pos]) goto bleep;
  1937.  
  1938.         dline.vused--;
  1939.         for(s2 = &string[field_pos]; *s2 != '\0'; s2++)
  1940.           *s2 = s2[1];
  1941.  
  1942.         *s2 = '\0';            /* Copy last NULL */
  1943.         line_paint(field_pos, &passwd);
  1944.         break;
  1945.  
  1946.  
  1947.             /*--------------- Kill line -----------------*/
  1948.           case ctrl('K'):
  1949.             if(kill_buffer != NULL)
  1950.               fs_give((void **)&kill_buffer);
  1951.  
  1952.             kill_buffer = cpystr(string);
  1953.             string[0] = '\0';
  1954.             field_pos = 0;
  1955.         dline.vused = 0;
  1956.         line_paint(field_pos, &passwd);
  1957.             break;
  1958.  
  1959.             /*------------------- Undelete line --------------------*/
  1960.           case ctrl('U'):
  1961.             if(kill_buffer == NULL)
  1962.               goto bleep;
  1963.  
  1964.             /* Make string so it will fit */
  1965.             kb = cpystr(kill_buffer);
  1966.             dprint(2, (debugfile,
  1967.                "Undelete: %d %d\n", strlen(string), field_len));
  1968.             if(strlen(kb) + strlen(string) > field_len) 
  1969.                 kb[field_len - strlen(string)] = '\0';
  1970.             dprint(2, (debugfile,
  1971.                "Undelete: %d %d\n", field_len - strlen(string),
  1972.                strlen(kb)));
  1973.                        
  1974.             if(string[field_pos] == '\0') {
  1975.                 /*--- adding to the end of the string ----*/
  1976.                 for(k = kb; *k; k++)
  1977.           string[field_pos++] = *k;
  1978.  
  1979.                 string[field_pos] = '\0';
  1980.             } else {
  1981.                 goto bleep;
  1982.                 /* To lazy to do insert in middle of string now */
  1983.             }
  1984.  
  1985.         dline.vused = strlen(string);
  1986.             fs_give((void **)&kb);
  1987.         line_paint(field_pos, &passwd);
  1988.             break;
  1989.             
  1990.  
  1991.         /*-------------------- Interrupt --------------------*/
  1992.       case ctrl('C'): /* ^C */ 
  1993.         if(F_ON(F_USE_FK,ps_global) || flags & OE_DISALLOW_CANCEL)
  1994.           goto bleep;
  1995.  
  1996.         goto cancel;
  1997.       case PF2:
  1998.         if(F_OFF(F_USE_FK,ps_global) || flags & OE_DISALLOW_CANCEL)
  1999.           goto bleep;
  2000.  
  2001.       cancel:
  2002.         return_v = 1;
  2003.         if(saved_original)
  2004.           strcpy(string, saved_original);
  2005.  
  2006.         break;
  2007.         
  2008.  
  2009.           case ctrl('A'):
  2010.       case KEY_HOME:
  2011.             /*-------------------- Start of line -------------*/
  2012.         line_paint(field_pos = 0, &passwd);
  2013.             break;
  2014.  
  2015.  
  2016.           case ctrl('E'):
  2017.       case KEY_END:
  2018.             /*-------------------- End of line ---------------*/
  2019.         line_paint(field_pos = dline.vused, &passwd);
  2020.             break;
  2021.  
  2022.  
  2023.         /*-------------------- Help --------------------*/
  2024.       case ctrl('G') : 
  2025.       case PF1:
  2026.         if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
  2027.         km_popped++;
  2028.         FOOTER_ROWS(ps_global) = 3;
  2029.         clearfooter(ps_global);
  2030.         EndInverse();
  2031.         draw_keymenu(km, bitmap, cols, 1-FOOTER_ROWS(ps_global),
  2032.             0, FirstMenu, 0);
  2033.         StartInverse();
  2034.         mark_keymenu_dirty();
  2035.         y_base = -3;
  2036.         dline.row = real_y_base = y_base + ps_global->ttyo->screen_rows;
  2037.         PutLine0(real_y_base, x_base, prompt);
  2038.         fs_resize((void **)&dline.dl, (size_t)dline.dlen + 1);
  2039.         memset((void *)dline.dl, 0, (size_t)(dline.dlen + 1));
  2040.         line_paint(field_pos, &passwd);
  2041.         break;
  2042.         }
  2043.  
  2044.         if(FOOTER_ROWS(ps_global) > 1){
  2045.         mark_keymenu_dirty();
  2046.         return_v = 3;
  2047.         }
  2048.         else
  2049.           goto bleep;
  2050.  
  2051.         break;
  2052.  
  2053.  
  2054.         /*-------- Misunderstood escape sequence? -------*/
  2055.           case NO_OP_COMMAND:
  2056.         continue;
  2057.  
  2058.  
  2059.           case NO_OP_IDLE:
  2060.             if(new_mail(0, 2, 0) < 0)    /* Keep mail stream alive */
  2061.               break;            /* no changes, get on with life */
  2062.             /* Else fall into redraw */
  2063.  
  2064.         /*-------------------- Redraw --------------------*/
  2065.       case ctrl('L'):
  2066.             /*---------------- re size ----------------*/
  2067.           case KEY_RESIZE:
  2068.             
  2069.         dline.row = real_y_base = y_base > 0 ? y_base :
  2070.                      y_base + ps_global->ttyo->screen_rows;
  2071.             EndInverse();
  2072.             ClearScreen();
  2073.             redraw_titlebar();
  2074.             if(ps_global->redrawer != (void (*)())NULL)
  2075.               (*ps_global->redrawer)();
  2076.  
  2077.             redraw_keymenu();
  2078.             StartInverse();
  2079.             
  2080.             PutLine0(real_y_base, x_base, prompt);
  2081.             cols     =  ps_global->ttyo->screen_cols;
  2082.             too_thin = 0;
  2083.             if(cols < x_base + prompt_len + 4) {
  2084.         Writechar(BELL, 0);
  2085.                 PutLine0(real_y_base, 0, "Screen's too thin. Ouch!");
  2086.                 too_thin = 1;
  2087.             } else {
  2088.         dline.col   = x_base + prompt_len;
  2089.         dline.dlen  = cols - (x_base + prompt_len + 1);
  2090.         fs_resize((void **)&dline.dl, (size_t)dline.dlen + 1);
  2091.         memset((void *)dline.dl, 0, (size_t)(dline.dlen + 1));
  2092.         line_paint(field_pos, &passwd);
  2093.             }
  2094.             fflush(stdout);
  2095.  
  2096.             dprint(9, (debugfile,
  2097.                     "optionally_enter  RESIZE new_cols:%d  too_thin: %d\n",
  2098.                        cols, too_thin));
  2099.             break;
  2100.  
  2101.       case PF3 :        /* input to potentially remap */
  2102.       case PF5 :
  2103.       case PF6 :
  2104.       case PF7 :
  2105.       case PF8 :
  2106.       case PF9 :
  2107.       case PF10 :
  2108.       case PF11 :
  2109.       case PF12 :
  2110.           if(F_ON(F_USE_FK,ps_global)
  2111.          && fkey_table[ch - PF1] != NO_OP_COMMAND)
  2112.         ch = fkey_table[ch - PF1]; /* remap function key input */
  2113.   
  2114.           default:
  2115.         if(escape_list){        /* in the escape key list? */
  2116.         for(j=0; escape_list[j].ch != -1; j++){
  2117.             if(escape_list[j].ch == ch){
  2118.             return_v = escape_list[j].rval;
  2119.             break;
  2120.             }
  2121.         }
  2122.  
  2123.         if(return_v != -10)
  2124.           break;
  2125.         }
  2126.  
  2127.         if(iscntrl(ch & 0x7f)){
  2128.        bleep:
  2129.         putc(BELL, stdout);
  2130.         continue;
  2131.         }
  2132.  
  2133.        ok_for_passwd:
  2134.         /*--- Insert a character -----*/
  2135.         if(dline.vused >= field_len)
  2136.           goto bleep;
  2137.  
  2138.         /*---- extending the length of the string ---*/
  2139.         for(s2 = &string[++dline.vused]; s2 - string > field_pos; s2--)
  2140.           *s2 = *(s2-1);
  2141.  
  2142.         string[field_pos++] = ch;
  2143.         line_paint(field_pos, &passwd);
  2144.             
  2145.     }   /*---- End of switch on char ----*/
  2146.     }
  2147.  
  2148. #ifdef    _WINDOWS
  2149.     mswin_allowpaste(MSWIN_PASTE_DISABLE);
  2150. #endif
  2151.     fs_give((void **)&dline.dl);
  2152.     if(saved_original) 
  2153.       fs_give((void *)&saved_original);
  2154.  
  2155.     if(kill_buffer)
  2156.       fs_give((void **)&kill_buffer);
  2157.  
  2158.     removing_trailing_white_space(string);
  2159.     EndInverse();
  2160.     MoveCursor(real_y_base, x_base); /* Move the cursor to show we're done */
  2161.     fflush(stdout);
  2162.     resume_busy_alarm();
  2163.     if(km_popped){
  2164.     FOOTER_ROWS(ps_global) = 1;
  2165.     clearfooter(ps_global);
  2166.     ps_global->mangled_body = 1;
  2167.     }
  2168.  
  2169.     return(return_v);
  2170. }
  2171.  
  2172.  
  2173. /*
  2174.  * line_paint - where the real work of managing what is displayed gets done.
  2175.  *              The passwd variable is overloaded: if non-zero, don't
  2176.  *              output anything, else only blat blank chars across line
  2177.  *              once and use this var to tell us we've already written the 
  2178.  *              line.
  2179.  */
  2180. void
  2181. line_paint(offset, passwd)
  2182.     int   offset;            /* current dot offset into line */
  2183.     int  *passwd;            /* flag to hide display of chars */
  2184. {
  2185.     register char *pfp, *pbp;
  2186.     register char *vfp, *vbp;
  2187.     int            extra = 0;
  2188. #define DLEN    (dline.vbase + dline.dlen)
  2189.  
  2190.     /*
  2191.      * for now just leave line blank, but maybe do '*' for each char later
  2192.      */
  2193.     if(*passwd){
  2194.     if(*passwd > 1)
  2195.       return;
  2196.     else
  2197.       *passwd == 2;        /* only blat once */
  2198.  
  2199.     extra = 0;
  2200.     MoveCursor(dline.row, dline.col);
  2201.     while(extra++ < dline.dlen)
  2202.       Writechar(' ', 0);
  2203.  
  2204.     MoveCursor(dline.row, dline.col);
  2205.     return;
  2206.     }
  2207.  
  2208.     /* adjust right margin */
  2209.     while(offset >= DLEN + ((dline.vused > DLEN) ? -1 : 1))
  2210.       dline.vbase += dline.dlen/2;
  2211.  
  2212.     /* adjust left margin */
  2213.     while(offset < dline.vbase + ((dline.vbase) ? 2 : 0))
  2214.       dline.vbase = max(dline.vbase - (dline.dlen/2), 0);
  2215.  
  2216.     if(dline.vbase){                /* off screen cue left */
  2217.     vfp = &dline.vl[dline.vbase+1];
  2218.     pfp = &dline.dl[1];
  2219.     if(dline.dl[0] != '<'){
  2220.         MoveCursor(dline.row, dline.col);
  2221.         Writechar(dline.dl[0] = '<', 0);
  2222.     }
  2223.     }
  2224.     else{
  2225.     vfp = dline.vl;
  2226.     pfp = dline.dl;
  2227.     if(dline.dl[0] == '<'){
  2228.         MoveCursor(dline.row, dline.col);
  2229.         Writechar(dline.dl[0] = ' ', 0);
  2230.     }
  2231.     }
  2232.  
  2233.     if(dline.vused > DLEN){            /* off screen right... */
  2234.     vbp = vfp + (long)(dline.dlen-(dline.vbase ? 2 : 1));
  2235.     pbp = pfp + (long)(dline.dlen-(dline.vbase ? 2 : 1));
  2236.     if(pbp[1] != '>'){
  2237.         MoveCursor(dline.row, dline.col+dline.dlen);
  2238.         Writechar(pbp[1] = '>', 0);
  2239.     }
  2240.     }
  2241.     else{
  2242.     extra = dline.dlen - (dline.vused - dline.vbase);
  2243.     vbp = &dline.vl[max(0, dline.vused-1)];
  2244.     pbp = &dline.dl[dline.dlen];
  2245.     if(pbp[0] == '>'){
  2246.         MoveCursor(dline.row, dline.col+dline.dlen);
  2247.         Writechar(pbp[0] = ' ', 0);
  2248.     }
  2249.     }
  2250.  
  2251.     while(*pfp == *vfp && vfp < vbp)            /* skip like chars */
  2252.       pfp++, vfp++;
  2253.  
  2254.     if(pfp == pbp && *pfp == *vfp){            /* nothing to paint! */
  2255.     MoveCursor(dline.row, dline.col + (offset - dline.vbase));
  2256.     return;
  2257.     }
  2258.  
  2259.     /* move backward thru like characters */
  2260.     if(extra){
  2261.     while(extra >= 0 && *pbp == ' ')         /* back over spaces */
  2262.       extra--, pbp--;
  2263.  
  2264.     while(extra >= 0)                /* paint new ones    */
  2265.       pbp[-(extra--)] = ' ';
  2266.     }
  2267.  
  2268.     if((vbp - vfp) == (pbp - pfp)){            /* space there? */
  2269.     while((*pbp == *vbp) && pbp != pfp)        /* skip like chars */
  2270.       pbp--, vbp--;
  2271.     }
  2272.  
  2273.     if(pfp != pbp || *pfp != *vfp){            /* anything to paint?*/
  2274.     MoveCursor(dline.row, dline.col + (int)(pfp - dline.dl));
  2275.  
  2276.     do
  2277.       Writechar((unsigned char)((vfp <= vbp && *vfp)
  2278.               ? ((*pfp = *vfp++) == TAB) ? ' ' : *pfp
  2279.               : (*pfp = ' ')), 0);
  2280.     while(++pfp <= pbp);
  2281.     }
  2282.  
  2283.     MoveCursor(dline.row, dline.col + (offset - dline.vbase));
  2284. }
  2285.  
  2286.  
  2287.  
  2288. /*----------------------------------------------------------------------
  2289.     Check to see if the given command is reasonably valid
  2290.   
  2291.   Args:  ch -- the character to check
  2292.  
  2293.  Result:  A valid command is returned, or a well know bad command is returned.
  2294.  
  2295.  ---*/
  2296. validatekeys(ch)
  2297.      int  ch;
  2298. {
  2299.     if(F_ON(F_USE_FK,ps_global)) {
  2300.     if(ch >= 'a' && ch <= 'z')
  2301.       return(KEY_JUNK);
  2302.     } else {
  2303.     if(ch >= PF1 && ch <= PF12)
  2304.       return(KEY_JUNK);
  2305.     }
  2306.     return(ch);
  2307. }
  2308.  
  2309.  
  2310.  
  2311. /*----------------------------------------------------------------------
  2312.   Prepend config'd commands to keyboard input
  2313.   
  2314.   Args:  ch -- pointer to storage for returned command
  2315.  
  2316.  Returns: TRUE if we're passing back a useful command, FALSE otherwise
  2317.  
  2318.  ---*/
  2319. int
  2320. process_config_input(ch)
  2321.     int *ch;
  2322. {
  2323.     static char firsttime = (char) 1;
  2324.  
  2325.     /* commands in config file */
  2326.     if(ps_global->initial_cmds && *ps_global->initial_cmds) {
  2327.     /*
  2328.      * There are a few commands that may require keyboard input before
  2329.      * we enter the main command loop.  That input should be interactive,
  2330.      * not from our list of initial keystrokes.
  2331.      */
  2332.     if(ps_global->dont_use_init_cmds)
  2333.       return(0);
  2334.  
  2335.     *ch = *ps_global->initial_cmds++;
  2336.     if(!*ps_global->initial_cmds && ps_global->free_initial_cmds){
  2337.         fs_give((void **)&(ps_global->free_initial_cmds));
  2338.         ps_global->initial_cmds = 0;
  2339.     }
  2340.  
  2341.     return(1);
  2342.     }
  2343.  
  2344.     if(firsttime) {
  2345.     firsttime = 0;
  2346.     if(ps_global->in_init_seq) {
  2347.         ps_global->in_init_seq = 0;
  2348.         ps_global->save_in_init_seq = 0;
  2349.         clear_cursor_pos();
  2350.         F_SET(F_USE_FK,ps_global,ps_global->orig_use_fkeys);
  2351.         /* draw screen */
  2352.         *ch = ctrl('L');
  2353.         return(1);
  2354.     }
  2355.     }
  2356.  
  2357.     return(0);
  2358. }
  2359.  
  2360.  
  2361.  
  2362. /*----------------------------------------------------------------------
  2363.     record and playback user keystrokes
  2364.   
  2365.   Args:  ch -- the character to record
  2366.      play -- flag to tell us to return first recorded char on tape
  2367.  
  2368.  Returns: either character recorded or played back or -1 to indicate
  2369.       end of recording
  2370.  
  2371.  ---*/
  2372. #define    TAPELEN    256
  2373.  
  2374. int
  2375. key_recorder(ch, play)
  2376.     int  ch;
  2377.     int  play;
  2378. {
  2379.     static int     tape[TAPELEN];
  2380.     static long  recorded = 0L;
  2381.     static short length  = 0;
  2382.  
  2383.     if(play){
  2384.     ch = length ? tape[(recorded + TAPELEN - length--) % TAPELEN] : -1;
  2385.     }
  2386.     else{
  2387.     tape[recorded++ % TAPELEN] = ch;
  2388.     if(length < TAPELEN)
  2389.       length++;
  2390.     }
  2391.  
  2392.     return(ch);
  2393. }
  2394.